<template>
	<div class="popover-container">
		<!-- 触发元素插槽 -->
		<div ref="triggerRef">
			<slot
				name="trigger"
				@click="togglePopover"
			/>
		</div>
  
		<!-- 弹出层 -->
		<transition name="fade">
			<div
				v-show="isOpen"
				ref="popoverRef"
				class="popover"
				:style="popoverStyle"
				:class="[adjustedPlacement]"
			>
				<slot name="content" />
			</div>
		</transition>
	</div>
</template>
  
<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
  
const props = defineProps({
    placement: {
        type: String,
        default: 'bottom',
        validator: v => ['top','bottom','left','right'].includes(v)
    },
    margin: {
        type: Number,
        default: 8
    }
});
  
const isOpen = ref(false);
const triggerRef = ref(null);
const popoverRef = ref(null);
const adjustedPlacement = ref(props.placement);
  
// 动态计算最佳位置
const calculatePosition = () => {
    if (!triggerRef.value || !popoverRef.value) return {};
    
    const triggerRect = triggerRef.value.getBoundingClientRect();
    const popoverRect = popoverRef.value.getBoundingClientRect();

    const positionOptions = {
        original: props.placement,
        fallbacks: getFallbackPositions(props.placement)
    };
  
    // 寻找第一个合适的位置
    const finalPosition = [positionOptions.original, ...positionOptions.fallbacks]
        .find(pos => hasEnoughSpace(pos, triggerRect, popoverRect));
  
    adjustedPlacement.value = finalPosition || positionOptions.original;
    
    return getPositionStyle(adjustedPlacement.value, triggerRect);
};
  
// 获取备选位置顺序
const getFallbackPositions = (placement) => {
    const positionMap = {
        top: ['bottom', 'left', 'right'],
        bottom: ['top', 'left', 'right'],
        left: ['right', 'top', 'bottom'],
        right: ['left', 'top', 'bottom']
    };
    return positionMap[placement] || [];
};
  
// 检查是否有足够空间
const hasEnoughSpace = (position, triggerRect, popoverRect) => {
    const margin = props.margin;
    switch(position) {
        case 'top':
            return triggerRect.top - popoverRect.height - margin > 0;
        case 'bottom':
            return viewportHeight - triggerRect.bottom - popoverRect.height - margin > 0;
        case 'left':
            return triggerRect.left - popoverRect.width - margin > 0;
        case 'right':
            return viewportWidth - triggerRect.right - popoverRect.width - margin > 0;
        default:
            return true;
    }
};
  
// 获取最终定位样式
const getPositionStyle = (position, triggerRect) => {
    const scrollY = window.scrollY;
    const baseStyle = {
        top: 0,
        left: 0,
        transform: ''
    };
  
    const positions = {
        top: {
            top: `${triggerRect.top + scrollY - popoverRef.value.offsetHeight - props.margin}px`,
            left: `${triggerRect.left + triggerRect.width/2}px`,
            transform: 'translateX(-50%)'
        },
        bottom: {
            top: `${triggerRect.bottom + scrollY + props.margin}px`,
            left: `${triggerRect.left + triggerRect.width/2}px`,
            transform: 'translateX(-50%)'
        },
        left: {
            top: `${triggerRect.top + scrollY + triggerRect.height/2}px`,
            left: `${triggerRect.left - popoverRef.value.offsetWidth - props.margin}px`,
            transform: 'translateY(-50%)'
        },
        right: {
            top: `${triggerRect.top + scrollY + triggerRect.height/2}px`,
            left: `${triggerRect.right + props.margin}px`,
            transform: 'translateY(-50%)'
        }
    };
  
    return positions[position] || baseStyle;
};
  
const popoverStyle = computed(() => calculatePosition());
  
// 窗口变化时重新计算
const handleResize = () => {
    if (isOpen.value) {
        calculatePosition();
    }
};
  
// 滚动时重新计算
const handleScroll = () => {
    if (isOpen.value) {
        calculatePosition();
    }
};
  
watch(isOpen, (newVal) => {
    if (newVal) {
        nextTick(() => {
            calculatePosition();
        });
    }
});
  
onMounted(() => {
    window.addEventListener('resize', handleResize);
    window.addEventListener('scroll', handleScroll, true);
});
  
onBeforeUnmount(() => {
    window.removeEventListener('resize', handleResize);
    window.removeEventListener('scroll', handleScroll, true);
});
</script>
  
  <style scoped>
  .popover {
    position: absolute;
    z-index: 9999;
    background: white;
    border-radius: 4px;
    box-shadow: 0 3px 6px rgba(0,0,0,0.16);
    min-width: 120px;
    transition: transform 0.2s ease;
  }
  
  /* 箭头样式 */
  .popover::after {
    content: '';
    position: absolute;
    width: 10px;
    height: 10px;
    background: white;
    transform: rotate(45deg);
  }
  
  .popover[class*="top"]::after {
    bottom: -5px;
    left: 50%;
  }
  .popover[class*="bottom"]::after {
    top: -5px;
    left: 50%;
  }
  .popover[class*="left"]::after {
    right: -5px;
    top: 50%;
  }
  .popover[class*="right"]::after {
    left: -5px;
    top: 50%;
  }
  </style>